package com.blacktea.magicapi.Interceptor;

import cn.hutool.core.lang.Validator;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.blacktea.magicapi.base.BaseConstant.MagicUiPermissionTypeEnum;
import com.blacktea.magicapi.entites.domain.*;
import com.blacktea.magicapi.entites.dto.UiUserDto;
import com.blacktea.magicapi.policy.magicapiuiuser.UiUserPolicContext;
import com.blacktea.magicapi.policy.magicapiuiuser.UiUserPolicy;
import com.blacktea.magicapi.service.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.ssssssss.magicapi.exception.MagicAPIException;
import org.ssssssss.magicapi.exception.MagicLoginException;
import org.ssssssss.magicapi.interceptor.Authorization;
import org.ssssssss.magicapi.interceptor.AuthorizationInterceptor;
import org.ssssssss.magicapi.interceptor.MagicUser;
import org.ssssssss.magicapi.model.ApiInfo;
import org.ssssssss.magicapi.model.DataSourceInfo;
import org.ssssssss.magicapi.model.FunctionInfo;
import org.ssssssss.magicapi.model.Group;

import javax.servlet.http.HttpServletRequest;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @description: 自定义UI界面鉴权
 * https://ssssssss.org/guide/custom/authorization.html
 *
 * @see org.ssssssss.magicapi.interceptor.AuthorizationInterceptor
 * @author: black tea
 * @date: 2021/12/31 16:22
 */
public class CustomAuthorizationInterceptor implements AuthorizationInterceptor {

    @Autowired
    private MguMagicUiUserRoleService userRoleService;
    @Autowired
    private MguMagicUiRolePermissionService rolePermissionService;
    @Autowired
    private MguMagicUiPermissionService permissionService;
    @Autowired
    private MguMagicUiUserService userService;
    @Autowired
    private MguMagicUiRoleService roleService;

    /** 同账号限制登录人数,null为不限制 **/
    private Integer limitLogin = null;
    /** token对应UI用户相关操作的策略环境 **/
    private UiUserPolicContext context = UiUserPolicContext.of();

    public CustomAuthorizationInterceptor() {

    }

    public CustomAuthorizationInterceptor(Integer limitLogin, UiUserPolicy uiUserPolicy) {
        this.limitLogin = limitLogin;
        this.context = UiUserPolicContext.of(uiUserPolicy);
    }

    public CustomAuthorizationInterceptor(Integer limitLogin) {
        this.limitLogin = limitLogin;
    }

    public CustomAuthorizationInterceptor(UiUserPolicContext context) {
        this.context = context;
    }

    /**
     * 配置是否需要登录
     */
    @Override
    public boolean requireLogin() {
        return true;
    }

    /**
     * 自定义登录方法
     *
     * @param username 用户名
     * @param password 密码
     */
    @Override
    public MagicUser login(String username, String password) throws MagicLoginException {
        MguMagicUiUser magicUiUser = this.getByUsernameAndPassword(username, password);
        context.checkCommonLogin(magicUiUser,this.limitLogin);
        String id = magicUiUser.getId();
        List<MguMagicUiPermission> rolePermissions = new ArrayList<>();
        Arrays.stream(MagicUiPermissionTypeEnum.values()).forEach(v ->
            {
                rolePermissions.addAll(this.getByRolePermissions(id,v.getCode()));
            }
        );
        UiUserDto uiUserDto =
                new UiUserDto(magicUiUser,this.getByRoles(id),rolePermissions);
        return new MagicUser(id,username,context.buildToken(uiUserDto));
    }

    /**
     * 登出,并移除当前 token 对应的用户
     * @see UiUserPolicy
     */
    @Override
    public void logout(String token) {
        this.context.removeByToken(token);
    }

    /**
     * 根据Token获取用户信息
     */
    @Override
    public MagicUser getUserByToken(String token) throws MagicLoginException {
        UiUserDto uiUserDto = this.context.getUser(token);
        if (Validator.isNull(uiUserDto) || Validator.isNull(uiUserDto.getUser())) {
            throw new MagicLoginException("token无效");
        }
        MguMagicUiUser user = uiUserDto.getUser();
        return new MagicUser(user.getId(),user.getUsername(),token);
    }

    /**
     * 是否拥有页面按钮的权限,是否允许访问
     *
     * @see MagicUiPermissionTypeEnum
     * @param magicUser	用户信息
     */
    @Override
    public boolean allowVisit(MagicUser magicUser, HttpServletRequest request, Authorization authorization) {
        return this.allowVisit(magicUser,request,authorization, MagicUiPermissionTypeEnum.PAGE_BUTTON_PERMISSION);
    }

    /**
     * 是否拥有对该接口的增删改权限
     *
     * @see MagicUiPermissionTypeEnum
     */
    @Override
    public boolean allowVisit(MagicUser magicUser, HttpServletRequest request, Authorization authorization, ApiInfo apiInfo) {
        return this.allowVisit(magicUser,request,authorization, MagicUiPermissionTypeEnum.INTERFACE_PERMISSION);
    }

    /**
     * 是否拥有对该函数的增删改权限
     * @see MagicUiPermissionTypeEnum
     */
    @Override
    public boolean allowVisit(MagicUser magicUser, HttpServletRequest request, Authorization authorization, FunctionInfo functionInfo) {
        return this.allowVisit(magicUser,request,authorization, MagicUiPermissionTypeEnum.FUNCTION_PERMISSION);
    }

    /**
     * 是否拥有对该分组的增删改权限
     * @see MagicUiPermissionTypeEnum
     */
    @Override
    public boolean allowVisit(MagicUser magicUser, HttpServletRequest request, Authorization authorization, Group group) {
        return this.allowVisit(magicUser,request,authorization, MagicUiPermissionTypeEnum.GROUPING_PERMISSION);
    }

    /**
     *  是否拥有对该数据源的增删改权限
     *  @see MagicUiPermissionTypeEnum
     */
    @Override
    public boolean allowVisit(MagicUser magicUser, HttpServletRequest request, Authorization authorization, DataSourceInfo dataSourceInfo) {
        return this.allowVisit(magicUser,request,authorization, MagicUiPermissionTypeEnum.DATASOURCE_PERMISSION);
    }

    public List<MguMagicUiUserRole> getUserRoles(String userId){
        QueryWrapper<MguMagicUiUserRole> userRoleQueryWrapper = new QueryWrapper<>();
        userRoleQueryWrapper.eq("user_id",userId);
        return userRoleService.list(userRoleQueryWrapper);
    }

    public List<MguMagicUiRole> getByRoles(String userId){
        List<MguMagicUiUserRole> userRoles = this.getUserRoles(userId);
        Set<String> roleIds = userRoles.stream().map(MguMagicUiUserRole::getRoleId).collect(Collectors.toSet());
        return roleService.listByIds(roleIds);
    }

    public List<MguMagicUiPermission> getByRolePermissions(String userId, Integer permissionType){
        // 获取角色id集合
        List<MguMagicUiUserRole> userRoles = this.getUserRoles(userId);
        Set<String> roleIds = userRoles.stream().map(MguMagicUiUserRole::getRoleId).collect(Collectors.toSet());
        // 获取权限id集合
        QueryWrapper<MguMagicUiRolePermission> rolePermissionQueryWrapper = new QueryWrapper<>();
        rolePermissionQueryWrapper.in("role_id",roleIds);
        List<MguMagicUiRolePermission> rolePermissions = rolePermissionService.list(rolePermissionQueryWrapper);
        Set<String> permissionIds = rolePermissions
                .stream()
                .map(MguMagicUiRolePermission::getPermissionsId)
                .collect(Collectors.toSet())
                ;
        // 获取权限对象集合
        QueryWrapper<MguMagicUiPermission> permissionQueryWrapper = new QueryWrapper<>();
        permissionQueryWrapper.in("id",permissionIds);
        permissionQueryWrapper.eq("type",permissionType);
        return permissionService.list(permissionQueryWrapper);
    }

    public MguMagicUiUser getByUsernameAndPassword(String username,String password) throws MagicLoginException {
        QueryWrapper<MguMagicUiUser> userQueryWrapper = new QueryWrapper<>();
        userQueryWrapper.eq("username",username);
        MguMagicUiUser magicUiUser = Optional
                .ofNullable(userService.getOne(userQueryWrapper))
                .orElseThrow(() -> new MagicAPIException("当前用户不存在"));
        if (!Validator.equal(password,magicUiUser.getPassword())){
            throw new MagicLoginException("密码不正确");
        }
        return magicUiUser;
    }

    public boolean allowVisit(MagicUser magicUser, HttpServletRequest request, Authorization authorization, MagicUiPermissionTypeEnum magicUiPermissionType) {

        Set<String> authorizations = this.getByRolePermissions(magicUser.getId(),magicUiPermissionType.getCode())
                .stream()
                .map(MguMagicUiPermission::getAuthorization)
                .collect(Collectors.toSet());
        return authorizations
                .stream()
                .anyMatch(a -> a.equalsIgnoreCase(authorization.name()));
    }
}
